using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Aspectize.Core;
using System.IO;

using OuSuisJe;
using Aspectize.GeoCoding;

namespace OuSuisJeWeb {
    public interface IOuSuisJeServices {

        DataSet CreateEnigma (DataSet ds);

        void UploadEnigmaPhoto(Stream data, Guid idPhoto, string fileName);

        DataSet LoadOpenEnigmas();

        bool CheckResponse(Guid idPhoto, string address, string zip, string city, string country, string comments);
    }

    [Service(Name = "OuSuisJeServices")]
    public class OuSuisJeServices : IOuSuisJeServices
    {
        internal const string OuSuisJeStorage = "OuSuisJeStorage";
        const double Million = 1000000.0;
        const double Threshold = 0.00001;
     
        #region IOuSuisJeServices Members
        
        DataSet IOuSuisJeServices.CreateEnigma(DataSet ds) {

            IDataManager dm = EntityManager.FromDataSetAndBaseService(ds, OuSuisJeStorage);
            IEntityManager em = dm as IEntityManager;
            
            var enigma = em.GetAllInstances<Enigma>() [0];

            var  provider = ExecutingContext.GetService<IGeoCodingServiceProvider>("GeoCoder");

            var geoCoded = provider.GeoCode(enigma.Address, enigma.PostalCode, enigma.City, enigma.Country);

            var longitude = -1.0;
            var latitude = -1.0;
            var places = geoCoded.Tables["Place"];

            if (places.Rows.Count > 0) {

                longitude = (double)places.Rows[0]["Longitude"];
                latitude = (double)places.Rows[0]["Latitude"];
            }

            enigma.Longitude = (long)(longitude * Million);
            enigma.Latitude = (long)(latitude * Million);

            var photo = em.CreateInstance<Photo>();

            photo.Id = enigma.Id;
            photo.IsResolved = false;
            photo.Name = enigma.Name;
            photo.PostDate = DateTime.UtcNow;

            enigma.AssociateInstance<PhotoEnigma>(photo);

            dm.SaveTransactional();

            return dm.Data;
        }
       
        void IOuSuisJeServices.UploadEnigmaPhoto(Stream data, Guid  idPhoto, string fileName) {

            IDataManager dm = EntityManager.FromDataBaseService(OuSuisJeStorage);

            var photo = dm.GetEntity<Photo>(idPhoto);

            var blob = ExecutingContext.GetService<IFileService>("BlobStorage");

            var path = idPhoto.ToString() + Path.GetExtension (fileName);

            blob.Write(path, data);
            photo.Url = blob.GetFileUrl(path);

            dm.SaveTransactional();
        }

        DataSet IOuSuisJeServices.LoadOpenEnigmas() {

            IDataManager dm = EntityManager.FromDataBaseService(OuSuisJeStorage);

            var criteria = new QueryCriteria(Photo.Fields.IsResolved, ComparisonOperator.Equal, false);

            dm.LoadEntities<Photo>(criteria);

            return dm.Data;
        }

        bool IOuSuisJeServices.CheckResponse(Guid idPhoto, string address, string zip, string city, string country, string comments) {

            IDataManager dm = EntityManager.FromDataBaseService(OuSuisJeStorage);

            dm.BuildSchema<Answer>();

            IEntityManager em = dm as IEntityManager;

            dm.LoadAssociated<Enigma, PhotoEnigma>(idPhoto);

            var enigma = em.GetInstance<Enigma>(idPhoto);
            var response = em.CreateInstance<Response>();

            enigma.AssociateInstance<Response, Answer>(response);

            response.Content = String.Format("It is here : {0} {1} {2} {3};\n{4}", address, zip, city, country, comments);
            response.PostDate = DateTime.UtcNow;
            response.IsExact = false;
            response.Latitude = response.Longitude = -1L;

            var eLongitude = enigma.Longitude / Million;
            var eLatitude = enigma.Latitude / Million;

            var geoCoded = geoCode(address, zip, city, country);
            IEntityManager geoEM = EntityManager.FromDataSet(geoCoded);
            var places = geoEM.GetAllInstances<GeoCoding.Place>();

            foreach (var place in places) {

                var rLongitude = place.Longitude;
                var rLatitude = place.Latitude;

                var dLongitude = (rLongitude - eLongitude);
                var dLatitude = (rLatitude - eLatitude);

                var d = Math.Sqrt(dLongitude * dLongitude + dLatitude * dLatitude);

                if (d < Threshold) {

                    response.Longitude = (long)(rLongitude * Million);
                    response.Latitude = (long)(rLatitude * Million);

                    response.IsExact = true;

                    var photo = enigma.GetAssociatedInstance<Photo, PhotoEnigma>();

                    photo.IsResolved = true;

                    break;
                }
            }

            dm.SaveTransactional();

            return response.IsExact;
        }

        DataSet geoCode(string address, string zipCode, string city, string country) {

           var provider = ExecutingContext.GetService<IGeoCodingServiceProvider>("GeoCoder");

           return provider.GeoCode(address, zipCode, city, country);
        }

        #endregion
    }

}
